#include "Utils/CirQueue.h"

#include <stdio.h>
#include <string.h> // strlen
#include <stdlib.h> // malloc

void CirQueue_Init(ST_CirQueue* a_pstCirQueue, C_U8* a_pu8Data, C_U16 a_u16Size)
{
    a_pstCirQueue->m_pu8Data = a_pu8Data;
    a_pstCirQueue->m_u16Size = a_u16Size;
    a_pstCirQueue->m_u16Front = 0;
    a_pstCirQueue->m_u16Rear = 0;
    a_pstCirQueue->m_u16Count = 0;
}

void CirQueue_DeInit(ST_CirQueue* a_pstCirQueue)
{
    a_pstCirQueue->m_pu8Data = NULL;
    a_pstCirQueue->m_u16Size = 0;
    a_pstCirQueue->m_u16Front = 0;
    a_pstCirQueue->m_u16Rear = 0;
    a_pstCirQueue->m_u16Count = 0;
}

C_BOOL CirQueue_Init_Malloc(ST_CirQueue* a_pstCirQueue, C_U16 a_u16Size)
{
    C_U8* pu8Data = NULL;

    pu8Data = malloc(a_u16Size);
    if (pu8Data == NULL)
    {
//        printf("CirQueue_Init_Malloc failed!\r\n");
        return C_FALSE;
    }

    a_pstCirQueue->m_pu8Data = pu8Data;
    a_pstCirQueue->m_u16Size = a_u16Size;
    a_pstCirQueue->m_u16Front = 0;
    a_pstCirQueue->m_u16Rear = 0;
    a_pstCirQueue->m_u16Count = 0;

    return C_TRUE;
}

void CirQueue_DeInit_Malloc(ST_CirQueue* a_pstCirQueue)
{
    free(a_pstCirQueue->m_pu8Data);
    a_pstCirQueue->m_pu8Data = NULL;
    a_pstCirQueue->m_u16Size = 0;
    a_pstCirQueue->m_u16Front = 0;
    a_pstCirQueue->m_u16Rear = 0;
    a_pstCirQueue->m_u16Count = 0;
}

void CirQueue_Clean(ST_CirQueue* a_pstCirQueue)
{
    a_pstCirQueue->m_u16Front = 0;
    a_pstCirQueue->m_u16Rear = 0;
    a_pstCirQueue->m_u16Count = 0;
}

C_BOOL CirQueue_isEmpty(ST_CirQueue* a_pstCirQueue)
{
    /*
    if (0 == a_pstCirQueue->m_u16Count)
    {
        return C_TRUE;
    }
    else
    {
        return C_FALSE;
    }
    */

    return a_pstCirQueue->m_u16Count == 0;
}

C_BOOL CirQueue_isFull(ST_CirQueue* a_pstCirQueue)
{
    return a_pstCirQueue->m_u16Count == a_pstCirQueue->m_u16Size;
}

C_U16 CirQueue_Count(ST_CirQueue* a_pstCirQueue)
{
    return a_pstCirQueue->m_u16Count;
}

C_U16 CirQueue_Remain(ST_CirQueue* a_pstCirQueue)
{
    return a_pstCirQueue->m_u16Size - CirQueue_Count(a_pstCirQueue);
}

C_S16 CirQueue_FindC(ST_CirQueue* a_pstCirQueue, C_ACHAR a_cChar)
{
    C_U16 u16Index = 0;

    for (u16Index = 0; u16Index < a_pstCirQueue->m_u16Count; u16Index++)
    {
        if (a_cChar == a_pstCirQueue->m_pu8Data[(a_pstCirQueue->m_u16Front + u16Index) % a_pstCirQueue->m_u16Size])
        {
            return u16Index;
        }
    }

    return -1;
}

C_S16 CirQueue_FindStr(ST_CirQueue* a_pstCirQueue, C_ACHAR* a_psStr)
{
    C_U16 u16Index = 0;
    C_U16 u16Pos1 = 0;
    C_U16 u16Pos2 = 0;

    C_U16 u16Len = strlen(a_psStr);

    if (u16Len == 0)
    {
        return 0;
    }

    if (u16Len > a_pstCirQueue->m_u16Count)
    {
        return -1;
    }

    for (u16Index = 0; u16Index <= a_pstCirQueue->m_u16Count - u16Len; u16Index++)
    {
        u16Pos1 = u16Index;
        u16Pos2 = 0;

        while (
                (u16Pos1 < a_pstCirQueue->m_u16Count)
                &&
                (a_pstCirQueue->m_pu8Data[(a_pstCirQueue->m_u16Front + u16Pos1) % a_pstCirQueue->m_u16Size] == a_psStr[u16Pos2])
              )
        {
            u16Pos1++;
            u16Pos2++;
        }

        if (a_psStr[u16Pos2] == '\0')
            return u16Index;
    }

    return -1;
}

C_S16 CirQueue_FindStrfrom(ST_CirQueue* a_pstCirQueue, C_ACHAR* a_psStr, C_U16 a_u16Offset)
{
    C_U16 u16Index = 0;
    C_U16 u16Pos1 = 0;
    C_U16 u16Pos2 = 0;

    C_U16 u16Len = strlen(a_psStr);

    if (u16Len == 0)
    {
        return 0;
    }

    if (u16Len > a_pstCirQueue->m_u16Count - a_u16Offset)
    {
        return -1;
    }

    for (u16Index = 0; u16Index <= a_pstCirQueue->m_u16Count- a_u16Offset - u16Len; u16Index++)
    {
        u16Pos1 = u16Index + a_u16Offset;
        u16Pos2 = 0;

        while (
                (u16Pos1 < a_pstCirQueue->m_u16Count)
                &&
                (a_pstCirQueue->m_pu8Data[(a_pstCirQueue->m_u16Front + u16Pos1) % a_pstCirQueue->m_u16Size] == a_psStr[u16Pos2])
              )
        {
            u16Pos1++;
            u16Pos2++;
        }

        if (a_psStr[u16Pos2] == '\0')
            return u16Index;
    }

    return -1;
}

C_BOOL CirQueue_En(ST_CirQueue* a_pstCirQueue, C_U8 a_u8Data)
{
    if (CirQueue_isFull(a_pstCirQueue))
    {
//        printf("CirQueue_En overflow!\r\n");
        return C_FALSE;
    }

    a_pstCirQueue->m_pu8Data[a_pstCirQueue->m_u16Rear] = a_u8Data;
    a_pstCirQueue->m_u16Rear = (a_pstCirQueue->m_u16Rear + 1) % a_pstCirQueue->m_u16Size;
    a_pstCirQueue->m_u16Count++;

    return C_TRUE;
}

C_BOOL CirQueue_EnBlock(ST_CirQueue* a_pstCirQueue, C_U8* a_pu8Data, C_U16 a_u16Length)
{
    C_U16 u16Index = 0;

    if (CirQueue_Remain(a_pstCirQueue) < a_u16Length)
    {
//        printf("CirQueue_EnBlock overflow, %d < %d!\r\n", CirQueue_Remain(a_pstCirQueue), a_u16Length);
        return C_FALSE;
    }

//	printf("rcv data:");
    for (u16Index = 0; u16Index < a_u16Length; u16Index++)
    {
//		printf("%x ",a_pu8Data[u16Index]);
        CirQueue_En(a_pstCirQueue, a_pu8Data[u16Index]);
    }
//	printf(" end\r\n");
    return C_TRUE;
}

C_BOOL CirQueue_De(ST_CirQueue* a_pstCirQueue, C_U8* a_pu8Data)
{
    if (CirQueue_isEmpty(a_pstCirQueue))
    {
//        printf("CirQueue_De underflow!\r\n");
        return C_FALSE;
    }

    if (a_pu8Data != NULL)
    {
        *a_pu8Data = a_pstCirQueue->m_pu8Data[a_pstCirQueue->m_u16Front];
    }
    a_pstCirQueue->m_u16Front = (a_pstCirQueue->m_u16Front + 1) % a_pstCirQueue->m_u16Size;
    a_pstCirQueue->m_u16Count--;

    return C_TRUE;
}

C_BOOL CirQueue_DeBlock(ST_CirQueue* a_pstCirQueue, C_U8* a_pu8Data, C_U16 a_u16Length)
{
    C_U16 u16Index = 0;

    if (CirQueue_Count(a_pstCirQueue) < a_u16Length)
    {
//        printf("CirQueue_DeBlock underflow, %d < %d!\r\n", CirQueue_Count(a_pstCirQueue), a_u16Length);
        return C_FALSE;
    }

    for (u16Index = 0; u16Index < a_u16Length; u16Index++)
    {
        if (a_pu8Data != NULL)
        {
            CirQueue_De(a_pstCirQueue, &a_pu8Data[u16Index]);
        }
        else
        {
            CirQueue_De(a_pstCirQueue, NULL);
        }
    }

    return C_TRUE;
}

C_BOOL CirQueue_Peek(ST_CirQueue* a_pstCirQueue, C_U8* a_pu8Data)
{
    if (CirQueue_isEmpty(a_pstCirQueue))
    {
//        printf("CirQueue_Get underflow!\r\n");
        return C_FALSE;
    }

    *a_pu8Data = a_pstCirQueue->m_pu8Data[a_pstCirQueue->m_u16Front];

    return C_TRUE;
}

C_BOOL CirQueue_PeekBlock(ST_CirQueue* a_pstCirQueue, C_U8* a_pu8Data, C_U16 a_u16Length)
{
    C_U16 u16Index = 0;

    if (CirQueue_Count(a_pstCirQueue) < a_u16Length)
    {
//        printf("CirQueue_GetBlock underflow, %d < %d!\r\n", CirQueue_Count(a_pstCirQueue), a_u16Length);
        return C_FALSE;
    }

    for (u16Index = 0; u16Index < a_u16Length; u16Index++)
    {
        a_pu8Data[u16Index] = a_pstCirQueue->m_pu8Data[(a_pstCirQueue->m_u16Front + u16Index) % a_pstCirQueue->m_u16Size];
    }

    return C_TRUE;
}

C_BOOL CirQueue_PeekBlockform(ST_CirQueue* a_pstCirQueue, C_U8* a_pu8Data, C_U16 a_u16Offset, C_U16 a_u16Length)
{
    C_U16 u16Index = 0;

    if (CirQueue_Count(a_pstCirQueue) - a_u16Offset < a_u16Length)
    {
//        printf("CirQueue_GetBlock underflow, %d < %d!\r\n", CirQueue_Count(a_pstCirQueue) - a_u16Offset, a_u16Length);
        return C_FALSE;
    }

    for (u16Index = 0; u16Index < a_u16Length; u16Index++)
    {
        a_pu8Data[u16Index] = a_pstCirQueue->m_pu8Data[(a_pstCirQueue->m_u16Front + a_u16Offset + u16Index) % a_pstCirQueue->m_u16Size];
    }

    return C_TRUE;
}

void CirQueue_Print(ST_CirQueue* a_pstCirQueue, C_U8 a_Types) // 0:Dec 1:Hex 2:Str
{
    C_U16 u16Index = 0;

//    printf("CirQueue[%d]:", a_pstCirQueue->m_u16Count);

    for (u16Index = 0; u16Index < a_pstCirQueue->m_u16Count; u16Index++)
    {
        if (0 == a_Types)
        {
//            printf("%4d", a_pstCirQueue->m_pu8Data[(a_pstCirQueue->m_u16Front + u16Index) % a_pstCirQueue->m_u16Size]);
        }
        else if (1 == a_Types)
        {
//            printf("%02x ", a_pstCirQueue->m_pu8Data[(a_pstCirQueue->m_u16Front + u16Index) % a_pstCirQueue->m_u16Size]);
        }
        else if (2 == a_Types)
        {
//            printf("%c", a_pstCirQueue->m_pu8Data[(a_pstCirQueue->m_u16Front + u16Index) % a_pstCirQueue->m_u16Size]);
        }
    }

//    printf("\r\n");
}

#if 0
void CirQueueTest(void)
{
    ST_CirQueue stCirQueue = {0};
    C_U8 u8EnData = 0;
    C_U8 u8DeData = 0;

    CirQueue_Init_Malloc(&stCirQueue, 8);

    printf("----- Front:%d, Rear:%d, Count:%d\r\n", stCirQueue.m_u16Front, stCirQueue.m_u16Rear, stCirQueue.m_u16Count);
    for (C_U8 i = 0; i < 9; i++)
    {
        u8EnData = '0' + i;
        CirQueue_EnBlock(&stCirQueue, &u8EnData, 1);
        CirQueue_Print(&stCirQueue, 2);
    }
    printf("----- Front:%d, Rear:%d, Count:%d\r\n", stCirQueue.m_u16Front, stCirQueue.m_u16Rear, stCirQueue.m_u16Count);
    for (C_U8 i = 0; i < 4; i++)
    {
        CirQueue_DeBlock(&stCirQueue, &u8DeData, 1);
        CirQueue_Print(&stCirQueue, 2);
    }
    printf("----- Front:%d, Rear:%d, Count:%d\r\n", stCirQueue.m_u16Front, stCirQueue.m_u16Rear, stCirQueue.m_u16Count);
    for (C_U8 i = 0; i < 9; i++)
    {
        u8EnData = 'a' + i;
        CirQueue_EnBlock(&stCirQueue, &u8EnData, 1);
        CirQueue_Print(&stCirQueue, 2);
    }
    printf("----- Front:%d, Rear:%d, Count:%d\r\n", stCirQueue.m_u16Front, stCirQueue.m_u16Rear, stCirQueue.m_u16Count);

    printf("findc:%d\r\n", CirQueue_FindC(&stCirQueue, 'd'));
    printf("finds:%d\r\n", CirQueue_FindStr(&stCirQueue, "67ab"));

}
#endif
